# Color math! Allows you to do semi-sane operations on colors, like add 
# or multiply them. 
class Color
  attr_accessor :hue, :saturation, :value, :alpha

  def Color.random
    self.new [rand, rand, rand, 1]
  end

  # Can be initialized either with another Color object, HSBA values in 
  # an array, or with an option hash
  def initialize opts = {}
    if opts.is_a? Color
      @hue, @saturation, @value, @alpha = opts.hue, opts.saturation, opts.value, opts.alpha
    elsif opts.is_a? Array
      @hue, @saturation, @value, @alpha = *opts
    else
      @hue = opts[:hue] || opts[:h]
      @saturation = opts[:saturation] || opts[:s]
      @value = opts[:value] || opts[:v]
      @alpha = opts[:alpha] || opts[:a]
    end
    @hue ||= 0
    @saturation ||= 0
    @value ||= 1
    @alpha ||= 1
    self
  end

  def == o
    return false if not o.is_a? Color
    (self.hue == o.hue) &&
    (self.saturation == o.saturation) &&
    (self.value == o.value) &&
    (self.alpha == o.alpha)
  end

  def === o
    return false if not o.is_a? Color
    (self.hue - o.hue).abs < 0.1 &&
    (self.saturation - o.saturation).abs < 0.1 &&
    (self.value - o.value).abs < 0.1 &&
    (self.alpha - o.alpha).abs < 0.1
  end

  def - f
    f = f.value if f.respond_to? :value
    space = @value.to_f
    new_value = (@value.to_f - space * f.to_f)
    return Color.new([@hue, @saturation, new_value, @alpha])
  end

  def % f
    f = f.alpha if f.respond_to? :alpha
    new_alpha = (@alpha.to_f + f.to_f) / 2.0
    return Color.new([@hue, @saturation, @value, new_alpha])
  end

  def + f
    f = f.value if f.respond_to? :value
    new_value = (@value.to_f + f.to_f) / 2.0
    return Color.new([@hue, @saturation, new_value, @alpha])
  end

  def / f
    f = f.saturation if f.respond_to? :saturation
    new_saturation = (@saturation.to_f + f.to_f) / 2.0
    return Color.new([@hue, new_saturation, @value, @alpha])
  end

  def ^ f
    f = f.hue if f.respond_to? :hue
    if (@hue.to_f - f.to_f).abs > 0.5 then
      temp_hue = (@hue.to_f + 1 + f.to_f) / 2.0
      if temp_hue > 1
        new_hue = temp_hue - 1
      else
        new_hue = temp_hue
      end
    else
      new_hue = (@hue.to_f + f.to_f) / 2.0
    end
    return Color.new([new_hue, @saturation, @value, @alpha])
  end

  def * f
    (((self % f) + f) / f)
  end

  def to_s
    "[#{@hue}, #{@saturation}, #{@value}, #{@alpha}]"
  end

  def to_a
    [@hue, @saturation, @value, @alpha]
  end
end

Red    ||= Color.new [0, 1, 1]
Blue   ||= Color.new [0.75, 1, 1]
Purple ||= Color.new [0.85, 1, 1]
Green  ||= Color.new [0.3, 1, 1]
Cyan   ||= Color.new [0.5, 1, 1]
Yellow ||= Color.new [0.2, 1, 1]
Black  ||= Color.new [0, 0, 0]
White  ||= Color.new [0, 0, 1]
Transparent ||= Color.new [0, 0, 0, 0]
Solid ||= Color.new [0, 0, 0, 1]
Gray10 ||= Color.new [0, 0, 0.1]
Gray20 ||= Color.new [0, 0, 0.2]
Gray30 ||= Color.new [0, 0, 0.3]
Gray40 ||= Color.new [0, 0, 0.4]
Gray50 ||= Color.new [0, 0, 0.5]
Gray60 ||= Color.new [0, 0, 0.6]
Gray70 ||= Color.new [0, 0, 0.7]
Gray80 ||= Color.new [0, 0, 0.8]
Gray90 ||= Color.new [0, 0, 0.9]
  
